package com.example.java_gobang.game;

import com.example.java_gobang.JavaGobangApplication;
import com.example.java_gobang.mapper.UserInfoMapper;
import com.example.java_gobang.model.UserInfo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.UUID;

//游戏房间
@Data
//@Component如果直接交给Spring管理的话这个类就变成单例的了，但是游戏房间肯定是要有多个的所以这样是不科学的
//虽然Spring也支持多例但是这个Room类已经被我们手动管理起来了，所以我们想要使用Spring管理的类应该使用手动注入的方法
public class Room {
    private String roomId;
    private UserInfo player1;
    private UserInfo player2;
    private int whiteUser;
    private final int MAX_ROW = 15;
    private final int MAX_COL = 15;
    //@Autowired
    private RoomManager roomManager;
    // @Autowired
    private ObjectMapper objectMapper;
    //@Autowired
    private OnlineUserManager onlineUserManager;
    private UserInfoMapper userInfoMapper;
    //约定：
    //0表示这个位置没有落子
    //1表示玩家1落子的地方
    //2表示玩家2落子的地方

    private int[][] board = new int[15][15];

    public Room() {
        this.roomId = UUID.randomUUID().toString();
        //通过入口类中的context来手动注入
        onlineUserManager = JavaGobangApplication.context.getBean(OnlineUserManager.class);
        roomManager = JavaGobangApplication.context.getBean(RoomManager.class);
        objectMapper = JavaGobangApplication.context.getBean(ObjectMapper.class);
        userInfoMapper = JavaGobangApplication.context.getBean(UserInfoMapper.class);
    }

    //1.记录落子
    //2.判断胜负
    //3.返回响应
    public void putChess(String payload) throws IOException {
        GameResponse response = new GameResponse();
        //1.记录落子
        GameRequest request = objectMapper.readValue(payload, GameRequest.class);
        int row = request.getRow();
        int col = request.getCol();
        int chess = (request.getUserId() == player1.getUserId() ? 1 : 2);
        if (board[row][col] != 0) {
            //位置已经有子
            return;
        }
        board[row][col] = chess;
        System.out.println("在(" + request.getCol() + "," + request.getRow() + ")处下子");
        //2.打印棋盘
        printBoard();
        //3.判断胜负
        //没有分出胜负返回0
        //分出胜负返回胜者id
        //平局返回-1
        int winner = checkWinner(row, col, chess);
        //4.返回响应
        response.setMessage("putChess");
        response.setRow(row);
        response.setCol(col);
        response.setWinner(winner);
        response.setUserId(request.getUserId());
        //给两名玩家都返回响应
        WebSocketSession session1 = onlineUserManager.getFromMatch(player1.getUserId());
        WebSocketSession session2 = onlineUserManager.getFromMatch(player2.getUserId());
        //处理掉线
        if (session1 == null) {
            System.out.println("玩家1掉线");
            response.setWinner(player2.getUserId());
        }
        if (session2 == null) {
            System.out.println("玩家2掉线");
            response.setWinner(player1.getUserId());
        }

        if (session1 != null) {
            session1.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }
        if (session2 != null) {
            session2.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }
        //5.如果已经分出胜负就把房间从房间管理器中销毁
        if (winner != 0) {
            if (winner == whiteUser) {

                System.out.println("游戏结束，小白子获胜");
            } else if (winner == -1) {
                System.out.println("游戏平局");
            } else {
                System.out.println("游戏结束，小黑子获胜");
            }
            if(winner != -1){
                //不平局的话更新胜者败者信息
                userInfoMapper.updateWinUser(winner);
                UserInfo loser = (winner == player1.getUserId()) ? player2 : player1;
                userInfoMapper.updateLoserUser(loser.getUserId());

                UserInfo win = userInfoMapper.selectById(winner);
                System.out.println("胜者:"+win.getUsername()+"败者:"+loser.getUsername());
            }
            //销毁房间
            roomManager.removeRoom(roomId, player1.getUserId(), player2.getUserId());
        }
    }

    private void printBoard() {
        System.out.println("[打印棋盘信息]" + roomId);
        System.out.println("==========================================================");
        for (int i = 0; i < MAX_ROW; i++) {
            for (int j = 0; j < MAX_COL; j++) {
                System.out.print(board[i][j] + "  ");
            }
            System.out.println();
        }
        System.out.println("==========================================================");
    }

    private int checkWinner(int row, int col, int chess) {
        //遍历所有行
        //其实在行上如果胜利只会有五种情况，也就是刚刚下的子分别在从左到右的1~5号位置
        //所有只要遍历这五种情况就行
        //方法是：以刚刚下的子为基础，依次把它作为第1~5号位置，并判断有没有出现五个子
        for (int c = col - 4; c <= col; ++c) {
            //c就是当前遍历结果下最左边的左边，当c为col-4时就是刚刚下的子是在五号位置这个结果
            //当c为col-3时就是刚刚下的位置是在四号位置这个结果，因为c代表的是当前结果下的最左边的左边
            //所以只需判断c和c右边四个坐标的棋子颜色一不一样就行
            //如果有数组越界的情况可以不用管因为c和其右边四个坐标有一个越界就说明，不会出现五子的情况
            try {
                if (board[row][c] == chess &&
                        board[row][c + 1] == chess &&
                        board[row][c + 2] == chess &&
                        board[row][c + 3] == chess &&
                        board[row][c + 4] == chess) {
                    return chess == 1 ? player1.getUserId() : player2.getUserId();
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }
        //检查所有列
        for (int r = row - 4; r <= row; ++r) {
            try {
                if (board[r][col] == chess &&
                        board[r + 1][col] == chess &&
                        board[r + 2][col] == chess &&
                        board[r + 3][col] == chess &&
                        board[r + 4][col] == chess) {
                    return chess == 1 ? player1.getUserId() : player2.getUserId();
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }
        //检擦左对角线
        for (int r = row - 4, c = col - 4; r <= row && c <= col; ++r, ++c) {
            try {
                if (board[r][c] == chess &&
                        board[r + 1][c + 1] == chess &&
                        board[r + 2][c + 2] == chess &&
                        board[r + 3][c + 3] == chess &&
                        board[r + 4][c + 4] == chess) {
                    return chess == 1 ? player1.getUserId() : player2.getUserId();
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }
        //检查右对角线
        for (int r = row - 4, c = col + 4; r <= row && c >= col; ++r, --c) {
            try {
                if (board[r][c] == chess &&
                        board[r + 1][c - 1] == chess &&
                        board[r + 2][c - 2] == chess &&
                        board[r + 3][c - 3] == chess &&
                        board[r + 4][c - 4] == chess) {
                    return chess == 1 ? player1.getUserId() : player2.getUserId();
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }
        //平局
        int flag = 1;//返回标志，如果还有空位置就置为0返回，如果没有就直接返回1
        for (int i = 0; i < MAX_ROW; i++) {
            for (int j = 0; j < MAX_COL; j++) {
                if (board[i][j] == 0) {
                    flag = 0;
                    break;
                }
            }
        }
        return flag;
    }
}
